import styles from "./index.less";
import {useAsyncEffect, useDebounceFn, useGetState, useMemoizedFn, useUnmount, useUnmountedRef} from "ahooks";
import {useRef} from "react";
import {formatDate} from "web-plugin-utils";

const WebRTCPlay = ({
                        streamUrl,
                        isPlay
                    }: {
    /**
     * 流地址
     */
    streamUrl: string;
    /**
     * 是否播放
     */
    isPlay: boolean;
}) => {
    const cache = false;
    const rangeVideoRef = useRef<HTMLVideoElement>(null);
    //远端流
    const [playIng, setPlayIng, getPlayIng] = useGetState(false);
    const [, setConnectIng, getConnectIng] = useGetState(false);
    const [, setPeer, getPeer] = useGetState<RTCPeerConnection>();
    const [, setVideoMediaStream, getVideoMediaStream] = useGetState<MediaStream>();
    const unmountRef: { current: boolean } = useUnmountedRef();

    const log = (log: string) => {
        console.log(
            `[${formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss')}][WebRTC] ${log}`,
        );
    };

    const destroyVideo = useMemoizedFn((force: boolean = false) => {
        const closePeer = () => {
            log(`销毁WebRTC连接`);
            // @ts-ignore
            if (cache && window[url]) {
                log(`销毁缓存连接`);
                // @ts-ignore
                const {peerConnection, mediaStream} = window[url] as {
                    peerConnection: RTCPeerConnection;
                    mediaStream: MediaStream;
                };
                mediaStream?.getTracks().forEach((t) => t.stop());
                peerConnection?.close();
                // @ts-ignore
                window[url] = undefined;
            }

            setConnectIng(false);
            setPlayIng(false);
            rangeVideoRef.current?.pause();
            getVideoMediaStream()
                ?.getTracks()
                .forEach((t) => t.stop());
            getPeer()?.close();
        };
        if (cache) {
            const connectionState = getPeer()?.connectionState;
            if (
                force ||
                (connectionState != undefined && connectionState != 'connected')
            ) {
                closePeer();
            }
        } else {
            closePeer();
        }
    });

    const { run: replay } = useDebounceFn(
        useMemoizedFn((logMessage?: string) => {
            if (getConnectIng() && !unmountRef.current) {
                if (logMessage) {
                    log(logMessage);
                }
                destroyVideo(true);
                setTimeout(playVideo, 1000);
            }
        }),
        { wait: 5000 },
    );

    const { run: replayBySdp } = useDebounceFn( useMemoizedFn(() => {
            if (!getPlayIng() && getConnectIng() && !unmountRef.current) {
                log('SDP交换成功后30秒没有正常播放，重置播放器');
                destroyVideo(true);
                setTimeout(playVideo, 1000);
            }
        }), { wait: 30000 },
    );
    const videoPlay = (
        videoElement: HTMLVideoElement,
        videoStream: MediaStream,
        cachePlay: boolean = false,
    ) => {
        videoElement.srcObject = videoStream;
        videoElement
            .play()
            .then(() => {
                setPlayIng(true);
                log(`播放成功`);
            })
            .catch(() => {
                setPlayIng(false);
                log(`播放失败`);
                replay(`视频播放失败，重置播放器`);
            });
        if (cachePlay) {
            replayBySdp();
        }
        videoElement.onplaying = () => {
            // log('onplaying');
            setPlayIng(true);
        };
        videoElement.ontimeupdate = () => {
            // log('ontimeupdate', e);
            replay(`视频流卡住，重置播放器`);
        };
    };
    const playForWebRTC = useMemoizedFn(async () => {
        let url = streamUrl;
        log(`Play WHEP URL: ${url}`);
        const videoStream = new MediaStream();
        const peer = new RTCPeerConnection();
        peerEvent(peer, videoStream);
        setVideoMediaStream(videoStream);
        setPeer(peer);
        if (cache) {
            // @ts-ignore
            window[url] = {
                peerConnection: peer,
                mediaStream: videoStream,
            };
        }

        try {
            const rtcSdp = await peer.createOffer();
            await peer.setLocalDescription(rtcSdp);
            const result = await fetch(url, {
                method: 'POST',
                // mode: 'cors',
                cache: 'no-cache',
                // credentials: 'include',
                redirect: 'follow',
                referrerPolicy: 'no-referrer',
                headers: {
                    'Content-Type': 'application/sdp',
                },
                body: rtcSdp.sdp,
            });
            const remoteSdp = await result.text();
            await peer.setRemoteDescription(
                new RTCSessionDescription({ type: 'answer', sdp: remoteSdp }),
            );
        } catch (e: any) {
            log(`SDP交换失败` + e.message);
            destroyVideo(true);
            setTimeout(playVideo, 1000);
        }

        return {
            peerConnection: peer,
            mediaStream: videoStream,
        };
    });
    const peerEvent = useMemoizedFn( (peer: RTCPeerConnection, videoStream: MediaStream) => {
            if (peer.getTransceivers().length <= 0) {
                peer.addTransceiver('video', { direction: 'recvonly' });
                peer.addTransceiver('audio', { direction: 'recvonly' });
            }
            peer.onconnectionstatechange = () => {
                log(`SDP Connect Status: ${peer.connectionState}`);
                if (peer.connectionState === 'connected') {
                    replayBySdp();
                } else if (
                    peer.connectionState === 'disconnected' ||
                    peer.connectionState === 'failed'
                ) {
                    replay('WebRTC断开，重置播放器');
                }
            };
            peer.ontrack = (e) => {
                videoStream.addTrack(e.track);
            };
        },
    );

    const playVideo = useMemoizedFn(async () => {
        if (!isPlay) {
            setConnectIng(false);
            return;
        }
        if (getConnectIng()) {
            return;
        }
        const videoElement = rangeVideoRef.current;
        if (!videoElement) {
            setTimeout(playVideo, 1000);
            return;
        }
        let videoStream: MediaStream;
        let peer: RTCPeerConnection;

        setPlayIng(false);
        setConnectIng(true);

        if (cache && window[url]) {
            const {peerConnection, mediaStream} = window[url] as {
                peerConnection: RTCPeerConnection;
                mediaStream: MediaStream;
            };

            if (peerConnection.connectionState === 'connected') {
                videoStream = mediaStream;
                peer = peerConnection;
                peerEvent(peer, videoStream);
                setVideoMediaStream(videoStream);
                setPeer(peer);
                log(`Cache Play MediaStream ${videoStream.active}`);
                videoPlay(videoElement, videoStream, true);
            } else {
                const {mediaStream} = await playForWebRTC();
                videoStream = mediaStream;
                videoPlay(videoElement, videoStream);
            }
        } else {
            const {mediaStream} = await playForWebRTC();
            videoStream = mediaStream;
            videoPlay(videoElement, videoStream);
        }
        return destroyVideo;
    });
    useAsyncEffect(playVideo as any, [isPlay]);
    useUnmount(destroyVideo);

    return (
        <>
            <video
                className={styles.video}
                ref={rangeVideoRef}
                controls={true}
                muted={false}
                disablePictureInPicture={true}
                disableRemotePlayback={true}
                controlsList="nodownload noremoteplayback"
            />
        </>
    );
}


export default WebRTCPlay;